﻿using System;
using System.Collections.Generic;
using FBProject.BusinessLogic;
using FBProject.BusinessLogic.MSProjectProxies;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FBProjectTest
{
    /// <summary>
    ///This is a test class for TaskSynchronizerTest and is intended
    ///to contain all TaskSynchronizerTest Unit Tests
    ///</summary>
    [TestClass()]
    public class TaskSynchronizerTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_NoDifferencesTest()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 20,
                RemainingWork = 20,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);

            IList<TaskDifference> expected = new List<TaskDifference>();

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_EverythingDifferent_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 360,
                RemainingWork = 240,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1a"), externalTask1);
            task.Milestone = "ProjectX I1a";
            task.Priority = 700;
            task.Estimate = 300;
            task.RemainingWork = 15;
            task.Resource = "John A. Smith";

            IList<TaskDifference> expected = new List<TaskDifference>(){
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.Milestone, "ProjectX I1a", "ProjectX I1"),
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.Name, "Task 1a", "Task 1"),
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.Priority, "700", "800"),
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.Estimate, "5.00", "6.00"),
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.RemainingWork, "0.25", "4.00"),
                new TaskDifference(1, "FB11111 Task 1a", string.Empty, TaskDifferenceType.Resource, "John A. Smith", "John Smith")
            };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_EverythingDifferentForIgnored_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 25,
                RemainingWork = 20,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1a"), externalTask1);
            task.Milestone = "ProjectX I1a";
            task.Priority = 700;
            task.Estimate = 15;
            task.RemainingWork = 10;
            task.Resource = "John A. Smith";
            task.IgnoreForComparison = true;

            IList<TaskDifference> expected = new List<TaskDifference>();

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_EverythingDifferentForCompleted_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1a",
                Milestone = "ProjectX I1a",
                Estimate = 25,
                RemainingWork = 20,
                Priority = 700,
                Resource = "John A. Smith",
                Completed = true
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1"), externalTask1);
            task.Milestone = "ProjectX I1";
            task.Priority = 800;
            task.Estimate = 10;
            task.RemainingWork = 0;
            task.Resource = "John Smith";

            IList<TaskDifference> expected = new List<TaskDifference>() { 
                new TaskDifference(1, "FB11111 Task 1", string.Empty, TaskDifferenceType.Milestone, "ProjectX I1", "ProjectX I1a"),
                new TaskDifference(1, "FB11111 Task 1", string.Empty, TaskDifferenceType.Name, "Task 1", "Task 1a"),
                new TaskDifference(1, "FB11111 Task 1", string.Empty, TaskDifferenceType.Priority, "800", "700")
            };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_CompletedInMSP_NotCompletedInFB_v1_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 20,
                RemainingWork = 20,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);
            task.Estimate = 15;
            task.RemainingWork = 0;

            IList<TaskDifference> expected = new List<TaskDifference>() { new TaskDifference(2, "FB11111 Task 1", string.Empty, TaskDifferenceType.NewTask) };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_CompletedInMSP_NotCompletedInFB_v2_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 20,
                RemainingWork = 20,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);
            task.RemainingWork = 0;

            task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);

            IList<TaskDifference> expected = new List<TaskDifference>();

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_CompletedInMSP_NotCompletedInFB_v3_Test()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 20,
                RemainingWork = 120,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);
            task.RemainingWork = 0;

            task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);
            task.Work = 25;
            task.RemainingWork = 90;
            task.Priority = 700;
            task.Resource = "John A. Smith";

            IList<TaskDifference> expected = new List<TaskDifference>(){
                new TaskDifference(2, "FB11111 Task 1", string.Empty, TaskDifferenceType.Priority, "700", "800"),
                new TaskDifference(2, "FB11111 Task 1", string.Empty, TaskDifferenceType.RemainingWork, "1.50", "2.00"),
                new TaskDifference(2, "FB11111 Task 1", string.Empty, TaskDifferenceType.Resource, "John A. Smith", "John Smith")
            };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_NewTaskTest()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var externalTask1 = new ExternalTask()
            {
                TaskNumber = "11111",
                Name = "Task 1",
                Milestone = "ProjectX I1",
                Estimate = 10,
                RemainingWork = 10,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            var externalTask2 = new ExternalTask()
            {
                TaskNumber = "11112",
                Name = "Task 2",
                Milestone = "ProjectX I1",
                Estimate = 11,
                RemainingWork = 11,
                Priority = 800,
                Resource = "John Smith",
                Completed = false
            };
            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();
            externalTasks.Add(externalTask1.TaskNumber, externalTask1);
            externalTasks.Add(externalTask2.TaskNumber, externalTask2);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: externalTask1.Name), externalTask1);

            IList<TaskDifference> expected = new List<TaskDifference>() { new TaskDifference(2, "FB11112 Task 2", string.Empty, TaskDifferenceType.NewTask) };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_DeletedTaskTest()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1"));
            task.TaskNumber = "11111";
            task.Milestone = "ProjectX I1";
            task.Work = 10;
            task.RemainingWork = 10;
            task.Priority = 800;
            task.IgnoreForComparison = false;
            task.Resource = "John Smith";

            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();

            IList<TaskDifference> expected = new List<TaskDifference>() { new TaskDifference(1, "FB11111 Task 1", string.Empty, TaskDifferenceType.DeletedTask) };

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_DeletedIgnoredTaskTest()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1"));
            task.TaskNumber = "11111";
            task.Milestone = "ProjectX I1";
            task.Work = 10;
            task.RemainingWork = 10;
            task.Priority = 800;
            task.IgnoreForComparison = true;
            task.Resource = "John Smith";

            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();

            IList<TaskDifference> expected = new List<TaskDifference>();

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        public void Synchronize_DeletedCompletedTaskTest()
        {
            var application = new MockObjects.MSProject.ApplicationMock();
            var tasks = new MockObjects.MSProject.TasksMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            var task = new MSTaskProxy(msProjectProxy, tasks.Add(Name: "Task 1"));
            task.TaskNumber = "11111";
            task.Milestone = "ProjectX I1";
            task.Work = 10;
            task.RemainingWork = 0;
            task.Priority = 800;
            task.IgnoreForComparison = false;
            task.Resource = "John Smith";

            IDictionary<string, ExternalTask> externalTasks = new Dictionary<string, ExternalTask>();

            IList<TaskDifference> expected = new List<TaskDifference>();

            RunSynchronize(msProjectProxy, tasks, externalTasks, expected, 10, 90);
        }

        /// <summary>
        ///A test for Synchronize
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Synchronize_NoExternalTasksTest()
        {
            var tasks = new MockObjects.MSProject.TasksMock();
            var application = new MockObjects.MSProject.ApplicationMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);

            int progressMin = 10;
            int progressMax = 90;
            TaskSynchronizer target = new TaskSynchronizer(msProjectProxy, progressMin, progressMax);

            target.Synchronize(null);
        }

        /// <summary>
        ///A test for TaskSynchronizer Constructor
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TaskSynchronizerConstructor_NoMSProjectProxyTest()
        {
            TaskSynchronizer target = new TaskSynchronizer(null, 0, 100);
        }

        /// <summary>
        ///A test for TaskSynchronizer Constructor
        ///</summary>
        [TestMethod()]
        public void TaskSynchronizerConstructorTest()
        {
            var tasks = new MockObjects.MSProject.TasksMock();
            var application = new MockObjects.MSProject.ApplicationMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);
            int progressMin = 11;
            int progressMax = 92;
            TaskSynchronizer_Accessor target = new TaskSynchronizer_Accessor(msProjectProxy, progressMin, progressMax);
            Assert.AreEqual<bool>(false, target.CancellationPending);
            Assert.AreEqual<int>(11, target.ProgressMin);
            Assert.AreEqual<int>(92, target.ProgressMax);
            Assert.AreEqual(msProjectProxy, target.MSProjectProxy);
        }

        private static void RunSynchronize(MSProjectProxy msProjectProxy, MockObjects.MSProject.TasksMock tasks, IDictionary<string, ExternalTask> externalTasks, IList<TaskDifference> expected, int progressMin, int progressMax)
        {
            TaskSynchronizer target = new TaskSynchronizer(msProjectProxy, progressMin, progressMax);

            IList<TaskDifference> actual = target.Synchronize(externalTasks);
            Assert.IsTrue(expected.Count >= actual.Count, "Number of reported differences ({0}) is greater then expected number ({1})", actual.Count, expected.Count);
            foreach (var taskDifference in expected)
            {
                Assert.IsTrue(actual.Contains(taskDifference), "TaskDifference \"{0}\" not reported", taskDifference);
            }
        }

        /// <summary>
        ///A test for Cancel
        ///</summary>
        [TestMethod()]
        public void CancelTest()
        {
            var tasks = new MockObjects.MSProject.TasksMock();
            var application = new MockObjects.MSProject.ApplicationMock();
            var project = new MockObjects.MSProject.ProjectMock(application, tasks);
            var msProjectProxy = new MSProjectProxy(application);
            int progressMin = 11;
            int progressMax = 92;
            TaskSynchronizer_Accessor target = new TaskSynchronizer_Accessor(msProjectProxy, progressMin, progressMax);
            Assert.AreEqual<bool>(false, target.CancellationPending);
            target.Cancel();
            Assert.AreEqual<bool>(true, target.CancellationPending);
        }
    }
}
